﻿package {
	import flash.display.MovieClip;
	import flash.display.Bitmap;
	import flash.utils.ByteArray;
	import flash.display.Stage;
	import flash.display.LoaderInfo;
	import flash.events.ProgressEvent;	

	public class Preloader extends MovieClip {		
		// Bitmap instnace
		private var img:BitmapImage;
		
		// Image dimensions
		private var imgWidth:Number;
		private var imgHeight:Number;
		
		// Tile size
		private var bitSizeW:Number = 2;
		private var bitSizeH:Number = 2;
		
		// movie clip to hold the image
		public var imgHolder:MovieClip;
		
		// Total number of tile pieces needed for whole image
		private var totalPieces:Number;
		
		// A count to keep track which tile has been triggered
		private var currentTileCount:int = 0;
		
		// Another count to keep track how many tile has completed its animation
		public var tileCompletedCount:int = 0;
		
		public function Preloader () {			
			/*
			From Paul Ortchanian at reflektions.com :
			"bitmaps in the library have required parameters for their constructor (since they are BitmapData instances and the BitmapData constructor requires a height and width).
			Whatever value used will be ignored and the actual bitmap size will be used in the resulting instance. Hence we use (0,0)"
			
			Once the Bitmap instance is created, we can assign the width & height properties to imgWidth & imgHeight, and then calculate the total pieces needed based on the tile size set above
			*/
			img = new BitmapImage (0, 0);
			imgWidth = img.width;
			imgHeight = img.height;
			totalPieces = (imgWidth * imgHeight) / (bitSizeW * bitSizeH);
						
			// Create a new movie clip instance, and place it on the center of the stage ('center' will be the image tiled at the center)
			imgHolder = new MovieClip();
			imgHolder.x = (stage.stageWidth / 2) - (imgWidth / 2);
			imgHolder.y = (stage.stageHeight / 2) - (imgHeight / 2);
			this.addChild (imgHolder);
			
			// once everything is initialized, create the little tiles
			generateSmallPieces ();
			
			// after all the little tiles are created, check if this movie has been loaded (cached?). If so, move all the pieces to its desinated location immediately.
			// otherwise, start listening to the download progress 
			if (this.loaderInfo.bytesLoaded == this.loaderInfo.bytesTotal) for (var i:int = 0; i < totalPieces; i++) movePiece();
			else this.loaderInfo.addEventListener (ProgressEvent.PROGRESS, onProgressData);
		}
		
		// internal private methods
		
		private function generateSmallPieces () : void {
			/*
			This method will calculate the number of tiles needed for each row and each column.
			Simply divide the tile's width with the image's width to find out how many columns are needed.
			Then divide the tile's height with the image's height to find out how many rows are needed.	
			*/
			var cols:Number = imgWidth / bitSizeW;
			var rows:Number = imgHeight / bitSizeH;
			// Then loop through each piece count to generate a tile
			for (var i:int = 0; i < totalPieces; i++) {
				// find out the row count by dividing the count number to number of columns needed. The integer without decimals will be the row #.
				var r:Number = Math.floor (i / cols);
				// find out the column # by getting the remainder from dividing the count number and columns. The remainder will always range from 0 - x, where x is the maximum column number on the grid.
				var c:Number = (i % cols);
				
				// finally, create a new instance of the "Piece" class, and add it to the empty movieclip placeholder.
				var tile:Piece = new Piece (bitSizeW, bitSizeH, c, r, img);
				imgHolder.addChild (tile);
				
				// assign a name to this tile, so we can reference it later by calling ["tile" + count], and tell it what to do.
				tile.name = "tile" + i;
			}
		}
		
		private function onProgressData (evt:ProgressEvent) : void {
			/*
			This method will be called whenever there's new data downloaded. First, calculate the percentage of the download progress.
			Then knowing the current percentage, tell all the associated pieces from the last percentage to the current percentage to move "movePiece()".
			ie. if there's 400 tiles in total, the first percent (0% - 1%) should be tile #0 - tile #3, so the first 4 tiles should be animated.
			And remember the last tile number (3 in this case), so the next time, say 1% - 2%, we know we should start at tile #4 - tile #7... and so on.
			*/
			var percent:Number = (evt.bytesLoaded / evt.bytesTotal);
			var percentPiece:Number = Math.floor (percent * totalPieces);
			for (var i:int = currentTileCount; i < percentPiece; i++) movePiece ();
		}
		
		private function movePiece () : void {
			/*
			This method will responsible for finding the current tile piece, and calling its animating method "moveToPosition()"
			Once this tile has been called, immediate increase the count number, so the next time when this method is called, it can point to the next tile immediately.
			*/
			var tile:Piece = imgHolder.getChildByName ("tile" + currentTileCount) as Piece;
			tile.moveToPosition();
			currentTileCount++;
		}
		
		// getter & setter methods
		
		public function get completedTween () : int {
			// this getter method will return the current count of completed tiles, so each "Piece" will able to increase the count as they finish its animation.
			return tileCompletedCount;
		}
		
		public function set completedTween (n:int) : void {
			// This setting method will set the current count of completed tiles, called from "Piece" class.
			// As soon as the count reaches to the same number as the total tile pieces "totalPieces", then run the proceedSite() method to continue on the rest of the Flash presentation.
			tileCompletedCount = n;
			if (n == totalPieces) {
				// also remove existing listeners that are no longer needed (loaderInfo, ProgressEvent.PROGRESS)
				this.loaderInfo.removeEventListener (ProgressEvent.PROGRESS, onProgressData);
				proceedSite();
			}
		}
		
		
		// method that will be triggered once the preloading & animations is all done
		private function proceedSite () : void {
			this.play();
		}
	}
}